'''
Created on Oct 7, 2012

@author: raygomez
'''
from ProtocolConstants import AVP_CODES

class AVP():
    
    AVP_VENDOR_FLAG     = 0x80
    AVP_MANDATORY_FLAG  = 0x40
    AVP_PRIVATE_FLAG    = 0x20
    
    def __init__(self,code=0,payload="",vendorId=0):
        self.payload = payload
        self.code = code
        self.flags = 0
        self.vendorId = vendorId
        
    def isVendorSpecific(self):
        """Returns if the AVP is vendor-specific (has non-zero vendorId")"""
        return self.vendorId != 0
    
    def isMandatory(self):
        """Returns if the mandatory (M) flag is set"""
        return (self.flags & AVP.AVP_MANDATORY_FLAG) != 0
    
    def isPrivate(self):
        """Returns if the private (P) flag is set"""
        return (self.flags & AVP.AVP_PRIVATE_FLAG) != 0
    
    def setMandatory(self, f):
        if f:
            self.flags |= AVP.AVP_MANDATORY_FLAG
        else:
            self.flags &= ~AVP.AVP_MANDATORY_FLAG
            
    def setPrivate(self, f):
        if f:
            self.flags |= AVP.AVP_PRIVATE_FLAG
        else:
            self.flags &= ~AVP.AVP_PRIVATE_FLAG
    
    @staticmethod
    def decodeSize(unpacker, remainingBytes):
        start = unpacker.get_position()
        if remainingBytes < 8:
            return 0
        unpacker.set_position(start + 4)
        flagsAndLength = unpacker.unpack_uint()
        unpacker.set_position(start)
        flags = flagsAndLength >> 24
        length = flagsAndLength & 0xFFFFFF
        paddedLength = (length + 3) & ~3
        minLength = 12 if flags & AVP.AVP_VENDOR_FLAG else 8
        if length < minLength:
            return 0
        return paddedLength
    
    def str_prefix__(self):
        s = ""
        if self.isVendorSpecific():
            s += ".v"
        if self.isMandatory():
            s += ".m"
        if self.isPrivate():
            s += ".p"
        if self.vendorId != 0:
            s += ":" + str(self.vendorId)
        return s
    
    def __str__(self):
        if self.code in AVP_CODES:
            avpCode = '\tAVP Code: {0}({1})\n'.format(AVP_CODES[self.code], self.code)
        else:
            avpCode = '\tAVP Code: {0}\n'.format(self.code)
        avpFlags = '\tAVP Flags:{0}\n'.format(self.str_prefix__())
        avpLength = '\tAVP Length:{0}\n'.format(self.length)
        data = '\t\tData:{0}\n'.format(self.payload)
        return avpCode + avpFlags + avpLength + data
        
    def decode(self, unpacker, remainingBytes):
        if remainingBytes < 8:
            return False
        i = 0
        self.code = unpacker.unpack_uint()
        i += 4
        flagsAndLength = unpacker.unpack_uint()
        i += 4
        self.flags = flagsAndLength >> 24
        self.length = flagsAndLength & 0xFFFFFF
        length = self.length
        paddedLength = (length +3) & ~3
        if remainingBytes != paddedLength:
            return False
        length -= 8
        
        if self.flags & AVP.AVP_VENDOR_FLAG:
            if length < 4:
                return False
            self.vendorId = unpacker.unpack_uint()
            i += 4
            length -= 4
        else:
            self.vendorId = 0
        self.payload = unpacker.unpack_fopaque(length)
    
        return True
    

    def encodeSize(self):
        size = 4 + 4
        if self.vendorId != 0:
            size +=4
        size += (len(self.payload) + 3) & ~3
        return size
    
    def encode(self, packer):
        size = 4 + 4
        if self.vendorId != 0:
            size += 4
        size += len(self.payload)
        
        f = self.flags
        if self.vendorId != 0:
            f |= AVP.AVP_VENDOR_FLAG
        else:
            f &= ~AVP.AVP_VENDOR_FLAG
        i = 0
        packer.pack_uint(self.code)
        i += 4
        packer.pack_uint(size | (f << 24))
        i += 4
        if self.vendorId != 0:
            packer.pack_uint(self.vendorId)
            i += 4
        self.length = len(self.payload)    
        paddedLength = (self.length + 3) & ~3
        packer.pack_fopaque(paddedLength, self.payload.encode("utf8"))
        i += paddedLength
        return i                
